# frozen_string_literal: true

module Svix
  class Webhook

    def self.new_using_raw_bytes(secret)
      self.new(secret.pack("C*").force_encoding("UTF-8"))
    end

    def initialize(secret)
      if secret.start_with?(SECRET_PREFIX)
        secret = secret[SECRET_PREFIX.length..-1]
      end

      @secret = Base64.decode64(secret)
    end

    def verify(payload, headers)
      msgId = headers["svix-id"]
      msgSignature = headers["svix-signature"]
      msgTimestamp = headers["svix-timestamp"]
      if !msgSignature || !msgId || !msgTimestamp
        msgId = headers["webhook-id"]
        msgSignature = headers["webhook-signature"]
        msgTimestamp = headers["webhook-timestamp"]
        if !msgSignature || !msgId || !msgTimestamp
          raise WebhookVerificationError, "Missing required headers"
        end
      end

      verify_timestamp(msgTimestamp)

      _, signature = sign(msgId, msgTimestamp, payload).split(",", 2)

      passedSignatures = msgSignature.split(" ")
      passedSignatures.each do |versionedSignature|
        version, expectedSignature = versionedSignature.split(",", 2)
        if version != "v1"
          next
        end

        if ::Svix::secure_compare(signature, expectedSignature)
          return JSON.parse(payload, symbolize_names: true)
        end
      end

      raise WebhookVerificationError, "No matching signature found"
    end

    def sign(msgId, timestamp, payload)
      begin
        now = Integer(timestamp)
      rescue
        raise WebhookSigningError, "Invalid timestamp"
      end

      toSign = "#{msgId}.#{timestamp}.#{payload}"
      signature = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), @secret, toSign)).strip
      return "v1,#{signature}"
    end

    private
    SECRET_PREFIX = "whsec_"
    TOLERANCE = 5 * 60

    def verify_timestamp(timestampHeader)
      begin
        now = Integer(Time.now)
        timestamp = Integer(timestampHeader)
      rescue
        raise WebhookVerificationError, "Invalid Signature Headers"
      end

      if timestamp < (now - TOLERANCE)
        raise WebhookVerificationError, "Message timestamp too old"
      end

      if timestamp > (now + TOLERANCE)
        raise WebhookVerificationError, "Message timestamp too new"
      end
    end
  end
end
